/* -------------------------------------------------------------------------------

This code is based on source provided under the terms of the Id Software 
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of 
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.

All changes and additions to the original source which have been developed by
other contributors (see CONTRIBUTORS) are provided under the terms of the
license the contributors choose (see LICENSE), to the extent permitted by the
LICENSE_ID. If you did not receive a copy of the contributor license,
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

----------------------------------------------------------------------------------

This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."

------------------------------------------------------------------------------- */



/* marker */
#define BSPFILE_RBSP_C



/* dependencies */
#include "q3map2.h"




/* -------------------------------------------------------------------------------

this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.

------------------------------------------------------------------------------- */

/* constants */
#define	LUMP_ENTITIES		0
#define	LUMP_SHADERS		1
#define	LUMP_PLANES			2
#define	LUMP_NODES			3
#define	LUMP_LEAFS			4
#define	LUMP_LEAFSURFACES	5
#define	LUMP_LEAFBRUSHES	6
#define	LUMP_MODELS			7
#define	LUMP_BRUSHES		8
#define	LUMP_BRUSHSIDES		9
#define	LUMP_DRAWVERTS		10
#define	LUMP_DRAWINDEXES	11
#define	LUMP_FOGS			12
#define	LUMP_SURFACES		13
#define	LUMP_LIGHTMAPS		14
#define	LUMP_LIGHTGRID		15
#define	LUMP_VISIBILITY		16
#define LUMP_LIGHTARRAY		17
#define	HEADER_LUMPS		18


/* types */
typedef struct
{
	char		ident[ 4 ];
	int			version;
	
	bspLump_t	lumps[ HEADER_LUMPS ];
}
rbspHeader_t;



/* light grid */
#define MAX_MAP_GRID		0xffff
#define MAX_MAP_GRIDARRAY	0x100000
#define LG_EPSILON			4


static void CopyLightGridLumps( rbspHeader_t *header )
{
	int				i;
	unsigned short	*inArray;
	bspGridPoint_t	*in, *out;
	
	
	/* get count */
	numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTARRAY, sizeof( *inArray ) );
	
	/* allocate buffer */
	bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
	memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
	
	/* copy */
	inArray = GetLump( (bspHeader_t*) header, LUMP_LIGHTARRAY );
	in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
	out = bspGridPoints;
	for( i = 0; i < numBSPGridPoints; i++ )
	{
		memcpy( out, &in[ *inArray ], sizeof( *in ) );
		inArray++;
		out++;
	}
}


static void AddLightGridLumps( FILE *file, rbspHeader_t *header )
{
	int				i, j, k, c, d;
	int				numGridPoints, maxGridPoints;
	bspGridPoint_t	*gridPoints, *in, *out;
	int				numGridArray;
	unsigned short	*gridArray;
	qboolean		bad;
	
	
	/* allocate temporary buffers */
	maxGridPoints = (numBSPGridPoints < MAX_MAP_GRID) ? numBSPGridPoints : MAX_MAP_GRID;
	gridPoints = safe_malloc( maxGridPoints * sizeof( *gridPoints ) );
	gridArray = safe_malloc( numBSPGridPoints * sizeof( *gridArray ) );
	
	/* zero out */
	numGridPoints = 0;
	numGridArray = numBSPGridPoints;
	
	/* for each bsp grid point, find an approximate twin */
	Sys_Printf( "Storing lightgrid: %d points\n", numBSPGridPoints );
	for( i = 0; i < numGridArray; i++ )
	{
		/* get points */
		in = &bspGridPoints[ i ];
		
		/* walk existing list */
		for( j = 0; j < numGridPoints; j++ )
		{
			/* get point */
			out = &gridPoints[ j ];
			
			/* compare styles */
			if( *((unsigned int*) in->styles) != *((unsigned int*) out->styles) )
				continue;
			
			/* compare direction */
			d = abs( in->latLong[ 0 ] - out->latLong[ 0 ] );
			if( d < (255 - LG_EPSILON) && d > LG_EPSILON )
				continue;
			d = abs( in->latLong[ 1 ] - out->latLong[ 1 ] );
			if( d < 255 - LG_EPSILON && d > LG_EPSILON )
				continue;
			
			/* compare light */
			bad = qfalse;
			for( k = 0; (k < MAX_LIGHTMAPS && bad == qfalse); k++ )
			{
				for( c = 0; c < 3; c++ )
				{
					if( abs( (int) in->ambient[ k ][ c ] - (int) out->ambient[ k ][ c ]) > LG_EPSILON ||
						abs( (int) in->directed[ k ][ c ] - (int) out->directed[ k ][ c ]) > LG_EPSILON )
					{
						bad = qtrue;
						break;
					}
				}
			}
			
			/* failure */
			if( bad )
				continue;
			
			/* this sample is ok */
			break;
		}
		
		/* set sample index */
		gridArray[ i ] = (unsigned short) j;
		
		/* if no sample found, add a new one */
		if( j >= numGridPoints && numGridPoints < maxGridPoints )
		{
			out = &gridPoints[ numGridPoints++ ];
			memcpy( out, in, sizeof( *in ) );
		}
	}
	
	/* swap array */
	for( i = 0; i < numGridArray; i++ )
		gridArray[ i ] = LittleShort( gridArray[ i ] );
	
	/* write lumps */
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, gridPoints, (numGridPoints * sizeof( *gridPoints )) );
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTARRAY, gridArray, (numGridArray * sizeof( *gridArray )) );
	
	/* free buffers */
	free( gridPoints );
	free( gridArray );
}



/*
LoadRBSPFile()
loads a raven bsp file into memory
*/

void LoadRBSPFile( const char *filename )
{
	rbspHeader_t	*header;
	
	
	/* load the file header */
	LoadFile( filename, (void**) &header );

	/* swap the header (except the first 4 bytes) */
	SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ) );
	
	/* make sure it matches the format we're trying to load */
	if( force == qfalse && *((int*) header->ident) != *((int*) game->bspIdent) )
		Error( "%s is not a %s file", filename, game->bspIdent );
	if( force == qfalse && header->version != game->bspVersion )
		Error( "%s is version %d, not %d", filename, header->version, game->bspVersion );
	
	/* load/convert lumps */
	numBSPShaders = CopyLump( (bspHeader_t*) header, LUMP_SHADERS, bspShaders, sizeof( bspShader_t ) );
	
	numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
	
	numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
	
	numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
	
	numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
	
	numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
	
	numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
	
	numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
	
	numBSPBrushSides = CopyLump( (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, sizeof( bspBrushSide_t ) );
	
	numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( bspDrawVerts[ 0 ] ) );
		SetDrawVerts( numBSPDrawVerts );
		CopyLump( (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, sizeof( bspDrawVerts[ 0 ] ) );
	
	numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( bspDrawSurfaces[ 0 ] ) );
		SetDrawSurfaces( numBSPDrawSurfaces );
		CopyLump( (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, sizeof( bspDrawSurfaces[ 0 ] ) );
	
	numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFogs[ 0 ] ) );
	
	numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
	
	numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
	
	numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
		bspLightBytes = safe_malloc( numBSPLightBytes );
		CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
	
	bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
	
	CopyLightGridLumps( header );
	
	/* free the file buffer */
	free( header );
}



/*
WriteRBSPFile()
writes a raven bsp file
*/

void WriteRBSPFile( const char *filename )
{		
	rbspHeader_t	outheader, *header;
	FILE			*file;
	time_t			t;
	char			marker[ 1024 ];
	int				size;
	
	
	/* set header */
	header = &outheader;
	memset( header, 0, sizeof( *header ) );
	
	//%	Swapfile();
	
	/* set up header */
	*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
	header->version = LittleLong( game->bspVersion );
	
	/* write initial header */
	file = SafeOpenWrite( filename );
	SafeWrite( file, (bspHeader_t*) header, sizeof( *header ) );	/* overwritten later */
	
	/* add marker lump */
	time( &t );
	sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t ) ) );
	AddLump( file, (bspHeader_t*) header, 0, marker, strlen( marker ) + 1 );
	
	/* add lumps */
	AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVerts[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
	AddLightGridLumps( file, header );
	AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
	AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ) );
	
	/* emit bsp size */
	size = ftell( file );
	Sys_Printf( "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
	
	/* write the completed header */
	fseek( file, 0, SEEK_SET );
	SafeWrite( file, header, sizeof( *header ) );
	
	/* close the file */
	fclose( file );	
}
